#ifndef __MY_UTIL__ // 进来，如果被定义了，就不会加载下面的内容了
#define __MY_UTIL__
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <experimental/filesystem>
#include <sys/stat.h>
#include <jsoncpp/json/json.h>
#include "bundle.h"

namespace cloud
{
    namespace fs = std::experimental::filesystem; // 目录遍历的操作
    class FileUtil                                // 文件实用工具类
    {
    private:
        std::string _filename;

    public:
        FileUtil(const std::string &filename) : _filename(filename) {}
        bool Remove()
        {
            if (this->Exists() == false)
            {
                return true;
            }
            remove(_filename.c_str());
            return true;
        }

        int64_t FileSize()
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0) // 通过文件路径名称，访问文件，获取文件状态属性信息，放入到st里去
            {
                std::cout << "get file size failed!\n";
                return -1;
            }
            return st.st_size;
        }
        time_t LastMtime() // 最后一次修改时间
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get file mtime failed!\n";
                return -1;
            }
            return st.st_mtime;
        }
        time_t LastAtime() // 最后一次访问时间
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get file atime failed!\n";
                return -1;
            }
            return st.st_atime;
        }
        std::string Filename()
        {
            // ./abc/test.txt
            size_t pos = _filename.find_last_of("/");
            if (pos == std::string::npos)
            {
                return _filename;
            }
            return _filename.substr(pos + 1); // 从pos+1位置截断，直到文件末尾
        }

        bool GetPostLen(std::string *body, size_t pos, size_t len) // 获取文件指定位置，指定长度的数据
        {
            size_t fsize = this->FileSize(); // 获取文件大小
            if (pos + len > fsize)           // 如果数据长度和指定位置不能大于文件大小
            {
                std::cout << "get file len is error\n";
                return false;
            }
            std::ifstream ifs;                     // 从文件里读取数据，输入（读）
            ifs.open(_filename, std::ios::binary); // 二进制方式读取文件数据
            if (ifs.is_open() == false)
            {
                std::cout << "read open file failed\n";
                return false;
            }

            ifs.seekg(pos, std::ios::beg); // 从文件起始位置开始偏移，偏移到pos位置处
            body->resize(len);             // 调整成len大小
            ifs.read(&(*body)[0], len);    // 读取len长度的数据，body是指针，要解引用后，再取[]，从_filename文件输入（读）到body中
            if (ifs.good() == false)
            {
                std::cout << "get file content failed\n";
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }

        bool GetContent(std::string *body) // 获取文件大小所以数据,body是输出型参数，是" "
        {
            size_t fsize = this->FileSize();
            return GetPostLen(body, 0, fsize);
        }

        bool SetContent(const std::string &body) // 写入文件数据,body是输入型参数
        {
            std::ofstream ofs;
            ofs.open(_filename, std::ios::binary); // 二进制的方式打开
            if (ofs.is_open() == false)
            {
                std::cout << "write open file failed\n";
                return false;
            }
            ofs.write(&body[0], body.size()); // 写入数据，从body写到_filename文件中
            if (ofs.good() == false)
            {
                std::cout << "write file content failed\n";
                return false;
            }
            ofs.close();
            return true;
        }

        bool Compress(const std::string &packname) // packname：压缩包名称
        {
            // 1、获取源文件数据
            std::string body;
            if (this->GetContent(&body) == false)
            {
                std::cout << "compress get file content failed\n";
                return false;
            }

            // 2、对数据进行压缩
            std::string packed = bundle::pack(bundle::LZIP, body);

            // 3、将压缩的数据存储到压缩包文件中
            FileUtil fu(packname);
            if (fu.SetContent(packed) == false)
            {
                std::cout << "compress write packed data failed\n";
                return false;
            }
            return true;
        }
        bool UnCompress(const std::string &filename)
        {
            // 1、将当前压缩包数据读取出来
            std::string body;
            if (this->GetContent(&body) == false)
            {
                std::cout << "uncompress get file content failed\n";
                return false;
            }
            // 2、对压缩的数据进行解压缩
            std::string unpacked = bundle::unpack(body);
            // 3、将解压缩的数据写入到新文件
            FileUtil fu(filename);
            if (fu.SetContent(unpacked) == false)
            {
                std::cout << "uncompress write packed data failed\n";
                return false;
            }
            return true;
        }
        bool Exists() // 判断文件是否存在
        {
            return fs::exists(_filename); // 不存在就返回一个false
        }
        bool CreateDirectory() // 创建目录
        {
            if (this->Exists())
                return true;
            return fs::create_directories(_filename); // create_directories：多层级目录的创建
            // create_directory：单层级目录的创建
        }
        bool ScanDirectory(std::vector<std::string> *arry) // 通过数组，返回目录下的所以文件的完整路径名称
        {
            for (auto &p : fs::directory_iterator(_filename))
            {
                if (fs::is_directory(p) == true) // is_directory：是否是一个目录的判断
                {
                    continue; // 是目录就遍历下一个
                }
                // relative_path 带有路径的文件名
                arry->push_back(fs::path(p).relative_path().string()); // 添加文件 .string()：获取string对象
            }
            return true;
        }
    };
    class JsonUtil // Json实用工具类
    {
    public:
        // const & 输入型（读）
        //* 输出型（写）
        static bool Serialize(const Json::Value &root, std::string *str) // 传入一个Json::Value,得到一个字符串，这是序列化
        {
            Json::StreamWriterBuilder swb;                                 // 实例化一个StreamWriterBuilder类
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter()); // 定义一个智能指针，他的类指向一个StreamWriter
            std::stringstream ss;
            if (sw->write(root, &ss) != 0) // 将root里的内容进行序列化，之后放入到ss的流里面,!=0说明失败了
            {
                std::cout << "json write failed\n";
                return false;
            }
            *str = ss.str();
            return true;
        }
        static bool UnSerialize(const std::string &str, Json::Value *root) // 传入一个字符串,得到一个Json::Value，这是反序列化
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);
            if (ret == false)
            {
                std::cout << "parse error:" << err << std::endl;
                return false;
            }
            return true;
        }
    };
}

#endif

// static函数没有this对象，直接调用接口就行了